package health_system.health_system.repository;


import health_system.health_system.entity.Follow;
import health_system.health_system.entity.User;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;

@Repository
public interface FollowRepository extends JpaRepository<Follow, Long> {
    
    @Query("SELECT f FROM Follow f WHERE f.follower.id = :followerId AND f.followed.id = :followedId")
    Optional<Follow> findByFollowerIdAndFollowedId(@Param("followerId") Long followerId, @Param("followedId") Long followedId);
    
    @Query("SELECT f.followed FROM Follow f WHERE f.follower.id = :userId")
    List<User> findFollowingByUserId(@Param("userId") Long userId);
    
    @Query("SELECT f.follower FROM Follow f WHERE f.followed.id = :userId")
    List<User> findFollowersByUserId(@Param("userId") Long userId);
    
    @Query("SELECT COUNT(f) FROM Follow f WHERE f.follower.id = :userId")
    Long countFollowingByUserId(@Param("userId") Long userId);
    
    @Query("SELECT COUNT(f) FROM Follow f WHERE f.followed.id = :userId")
    Long countFollowersByUserId(@Param("userId") Long userId);
    
    @Query("SELECT CASE WHEN COUNT(f) > 0 THEN true ELSE false END FROM Follow f WHERE f.follower.id = :followerId AND f.followed.id = :followedId")
    boolean isFollowing(@Param("followerId") Long followerId, @Param("followedId") Long followedId);
    
    @Modifying
    @Query("UPDATE Follow f SET f.isNotificationEnabled = :enabled WHERE f.id = :id")
    void updateNotificationEnabled(@Param("id") Long id, @Param("enabled") Boolean enabled);
    
    @Query("SELECT f FROM Follow f WHERE f.follower.id = :followerId AND f.followed.id IN :followedIds")
    List<Follow> findByFollowerIdAndFollowedIds(@Param("followerId") Long followerId, @Param("followedIds") List<Long> followedIds);
    
    @Query("SELECT f FROM Follow f WHERE f.follower.id IN :followerIds AND f.followed.id = :followedId")
    List<Follow> findByFollowerIdsAndFollowedId(@Param("followerIds") List<Long> followerIds, @Param("followedId") Long followedId);
    
    @Query("SELECT DISTINCT f.followed.id FROM Follow f WHERE f.follower.id = :userId")
    List<Long> findFollowedIdsByFollowerId(@Param("userId") Long userId);
    
    void deleteByFollowerIdAndFollowedId(Long followerId, Long followedId);
    
    // Find follow relationship between two users
    Optional<Follow> findByFollowerAndFollowed(User follower, User followed);
    
    // Find all users that a user is following
    @Query("SELECT f.followed FROM Follow f WHERE f.follower = :follower")
    List<User> findFollowingByFollower(@Param("follower") User follower);
    
    // Find all users that are following a user
    @Query("SELECT f.follower FROM Follow f WHERE f.followed = :followed")
    List<User> findFollowersByFollowed(@Param("followed") User followed);
    
    // Find all follow relationships where user is the follower
    List<Follow> findByFollower(User follower);
    
    // Find all follow relationships where user is being followed
    List<Follow> findByFollowed(User followed);
    
    // Find followers with pagination
    Page<Follow> findByFollowed(User followed, Pageable pageable);
    
    // Find following with pagination
    Page<Follow> findByFollower(User follower, Pageable pageable);
    
    // Count followers
    Long countByFollowed(User followed);
    
    // Count following
    Long countByFollower(User follower);
    
    // Check if a user follows another user
    boolean existsByFollowerAndFollowed(User follower, User followed);
    
    // Find mutual follows (users who follow each other)
    @Query("SELECT f1.followed FROM Follow f1 WHERE f1.follower = :user AND EXISTS " +
           "(SELECT f2 FROM Follow f2 WHERE f2.follower = f1.followed AND f2.followed = :user)")
    List<User> findMutualFollows(@Param("user") User user);
    
    // Find followers sorted by when they followed
    @Query("SELECT f.follower FROM Follow f WHERE f.followed = :user ORDER BY f.createdAt DESC")
    List<User> findFollowersOrderByCreatedAtDesc(@Param("user") User user);
    
    // Find following sorted by when they were followed
    @Query("SELECT f.followed FROM Follow f WHERE f.follower = :user ORDER BY f.createdAt DESC")
    List<User> findFollowingOrderByCreatedAtDesc(@Param("user") User user);
    
    // Find suggested users to follow (users who follow the users that the current user follows)
    @Query("SELECT DISTINCT f2.follower FROM Follow f1 JOIN Follow f2 ON f1.followed = f2.followed " +
           "WHERE f1.follower = :user AND f2.follower != :user AND NOT EXISTS " +
           "(SELECT f3 FROM Follow f3 WHERE f3.follower = :user AND f3.followed = f2.follower)")
    List<User> findSuggestedUsersToFollow(@Param("user") User user);
    
    // Find popular users (most followed)
    @Query("SELECT f.followed, COUNT(f) AS followCount FROM Follow f " +
           "GROUP BY f.followed ORDER BY followCount DESC")
    List<Object[]> findPopularUsers(Pageable pageable);
    
    // Find users with similar follows
    @Query("SELECT f2.follower, COUNT(f2) AS commonCount FROM Follow f1 JOIN Follow f2 ON f1.followed = f2.followed " +
           "WHERE f1.follower = :user AND f2.follower != :user " +
           "GROUP BY f2.follower ORDER BY commonCount DESC")
    List<Object[]> findUsersWithSimilarFollows(@Param("user") User user, Pageable pageable);
    
    // Search among followers
    @Query("SELECT f.follower FROM Follow f WHERE f.followed = :user AND f.follower.fullName LIKE %:searchTerm%")
    List<User> searchFollowers(@Param("user") User user, @Param("searchTerm") String searchTerm);
    
    // Search among following
    @Query("SELECT f.followed FROM Follow f WHERE f.follower = :user AND f.followed.fullName LIKE %:searchTerm%")
    List<User> searchFollowing(@Param("user") User user, @Param("searchTerm") String searchTerm);
    
    // Find followers who are also friends
    @Query("SELECT f.follower FROM Follow f WHERE f.followed = :user AND EXISTS " +
           "(SELECT fr FROM Friendship fr WHERE ((fr.requester = :user AND fr.addressee = f.follower) OR " +
           "(fr.requester = f.follower AND fr.addressee = :user)) AND fr.status = 'ACCEPTED')")
    List<User> findFollowersWhoAreFriends(@Param("user") User user);
    
    // Find followings who are also friends
    @Query("SELECT f.followed FROM Follow f WHERE f.follower = :user AND EXISTS " +
           "(SELECT fr FROM Friendship fr WHERE ((fr.requester = :user AND fr.addressee = f.followed) OR " +
           "(fr.requester = f.followed AND fr.addressee = :user)) AND fr.status = 'ACCEPTED')")
    List<User> findFollowingsWhoAreFriends(@Param("user") User user);
} 